package org.jeecg.modules.quartz.job;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jeecg.modules.quartz.entity.SysJobQueue;
import org.jeecg.modules.quartz.entity.SysJobSign;
import org.jeecg.modules.quartz.service.ISysJobQueueService;
import org.jeecg.modules.quartz.service.ISysJobSignService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.google.api.client.util.Maps;
import com.google.common.collect.Lists;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import io.finer.erp.jeecg.bas.entity.BasMaterial;
import io.finer.erp.jeecg.bas.entity.ZrkQzOrder;
import io.finer.erp.jeecg.bas.service.IBasMaterialService;
import io.finer.erp.jeecg.bas.service.IZrkQzOrderService;
import io.finer.erp.jeecg.stock.entity.StkInventory;
import io.finer.erp.jeecg.stock.entity.StkIoBill;
import io.finer.erp.jeecg.stock.entity.StkIoBillEntry;
import io.finer.erp.jeecg.stock.entity.StkIoMaterialLog;
import io.finer.erp.jeecg.stock.service.IStkInventoryService;
import io.finer.erp.jeecg.stock.service.IStkIoBillEntryService;
import io.finer.erp.jeecg.stock.service.IStkIoBillService;
import io.finer.erp.jeecg.stock.service.IStkIoMaterialLogService;
import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName MaterialCostJob
 * @Description 计算成本定时任务
 * @author duany
 * @Date 2021-04-13 15:13:26
 * @version 1.0.0
 */
@Slf4j
@Transactional
public class MaterialCostJob implements Job {
	@Autowired
	private ISysJobSignService jobSignService;
	@Autowired
	private ISysJobQueueService queueService;
	@Autowired
	private IStkIoBillService billService;
	@Autowired
	private IStkIoBillEntryService billEntryService;
	@Autowired
	private IBasMaterialService materialService;
	@Autowired
	private IStkIoMaterialLogService logService;
	@Autowired
	private IStkInventoryService inventoryService;
	@Autowired
	private IZrkQzOrderService qzOrderService;
	
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		log.info("执行每日成本核算定时任务" + DateUtil.now());
		String signCode = "material_cost_sign";
		String queueCode = "purchase_bill_price_change";
		
		//获取比当前大的
		SysJobSign sign = jobSignService.lambdaQuery().eq(SysJobSign::getJobSignCode, signCode).one();
		if(null == sign) {
			sign = new SysJobSign();
			sign.setSignNo(BigDecimal.ZERO.toString());
			sign.setJobSignCode(signCode);
			this.jobSignService.save(sign);
		}
		List<String> ioType = CollUtil.newArrayList("1991", "1992", "1993", "1994", "2991", "2992", "2993", "202");
		String signNo = sign.getSignNo();
		List<StkIoBill> billList = this.billService.lambdaQuery().in(StkIoBill::getStockIoType, ioType).gt(StkIoBill::getId, signNo).orderByAsc(StkIoBill::getId).list();
		if(CollUtil.isNotEmpty(billList)) {
			List<String> billIdList = Lists.newArrayList();
			List<String> mesIds = Lists.newArrayList();
			for (Iterator<StkIoBill> iterator = billList.iterator(); iterator.hasNext();) {
				StkIoBill bill = iterator.next();
				billIdList.add(bill.getId());
				String sourceId = bill.getSourceId();
				if(StrUtil.isBlank(sourceId)) {
					String sourceNo = bill.getSourceNo();
					if(StrUtil.isNotBlank(sourceNo)) {
						ZrkQzOrder mes = this.qzOrderService.getByCode(sourceNo);
						if(null != mes) {
							mesIds.add(mes.getId());
						}
					}
				} else {
					mesIds.add(sourceId);
				}
			}
			this.dealwithMesCost(mesIds);
			String maxBillId = null;
			for (String billId : billIdList) {
				if(maxBillId == null) {
					maxBillId = billId;
				} else {
					BigDecimal billDecimal = new BigDecimal(billId);
					BigDecimal maxDecimal = new BigDecimal(maxBillId);
					if(billDecimal.compareTo(maxDecimal) > 0) {
						maxBillId = billId;
					}
				}
			}
			if(null != maxBillId) {
				sign.setSignNo(signNo);
				this.jobSignService.updateById(sign);
			}
		}
		
		//计算因为采购单单价修改对物料的变动。
		List<SysJobQueue> priceChangeQueueList = this.queueService.getByCode(queueCode);
		List<String> queueIds = Lists.newArrayList();
		queueIds.add(UUID.randomUUID().toString());
		if(CollUtil.isNotEmpty(priceChangeQueueList)) {
			List<String> saleOutBillIds = Lists.newArrayList();
			for(Iterator<SysJobQueue> it = priceChangeQueueList.iterator(); it.hasNext();) {
				SysJobQueue queue = it.next();
				saleOutBillIds.add(queue.getQueueInfo());
			}
			List<StkIoBill> purchaseBillList = billService.lambdaQuery().eq(StkIoBill::getStockIoType, "601").in(StkIoBill::getId, saleOutBillIds).list();
			this.dealwithPurchasePriceChange(purchaseBillList);
		}
		
		this.queueService.removeByIds(queueIds);
		log.info("每日成本核算定时任务完成" + DateUtil.now());
	}
	
	private void dealwithPurchasePriceChange(List<StkIoBill> billList) {
		List<StkIoBillEntry> updateEntrys = Lists.newArrayList();
		if(CollUtil.isNotEmpty(billList)) {
			for(Iterator<StkIoBill> it = billList.iterator(); it.hasNext();) {
				StkIoBill purchaseBill = it.next();
				List<StkIoBill> bill101List = billService.lambdaQuery().eq(StkIoBill::getSourceNo, purchaseBill.getBillNo()).list();
				if(CollUtil.isEmpty(bill101List)) {
					continue;
				}
				List<StkIoBillEntry> purchaseEntryList = billEntryService.selectByMainId(purchaseBill.getId());
				if(CollUtil.isEmpty(purchaseEntryList)) {
					continue;
				}
				Map<String, BigDecimal> map = Maps.newHashMap();
				for (StkIoBillEntry stkIoBillEntry : purchaseEntryList) {
					String materialCategoryId = stkIoBillEntry.getMaterialId();
					BigDecimal decimal = stkIoBillEntry.getSettlePrice();
					map.put(materialCategoryId, decimal);
				}
				for(StkIoBill bill : bill101List) {
					String billId = bill.getId();
					List<StkIoBillEntry> entryList = this.billEntryService.selectByMainId(billId);
					if(null != entryList) {
						for (StkIoBillEntry entry : purchaseEntryList) {
							String materialId = entry.getMaterialId();
							BasMaterial material = this.materialService.getById(materialId);
							String categoryId = material.getCategoryId();
							BigDecimal price = map.get(categoryId);
							if(null != price) {
								BigDecimal settlePrice = entry.getSettlePrice();
								if(null != settlePrice && settlePrice.compareTo(price) != 0) {
									entry.setSettlePrice(price);
									BigDecimal qty = entry.getQty();
									BigDecimal cost = qty.multiply(price);
									cost = cost.setScale(2, BigDecimal.ROUND_DOWN);
									entry.setCost(cost);
									entry.setSettleAmt(cost);
									this.billEntryService.updateById(entry);
									updateEntrys.add(entry);
								}
							}
						}
					}
				}
				
			}
		}
		if(CollUtil.isNotEmpty(updateEntrys)) {
			for (StkIoBillEntry entry : updateEntrys) {
				String materialId = entry.getMaterialId();
				List<StkIoMaterialLog> logList = logService.lambdaQuery().eq(StkIoMaterialLog::getMaterialId, materialId).orderByAsc(StkIoMaterialLog::getCreateTime).list();
				if(CollUtil.isEmpty(logList)) {
					continue;
				}
				List<StkIoMaterialLog> log101List = Lists.newArrayList();
				List<String> mesIds = Lists.newArrayList();
				for (StkIoMaterialLog log : logList) {
					String ioType = log.getIoType();
					String procedureId = log.getProcedureId();
					if(StrUtil.isNotBlank(procedureId) && !mesIds.contains(procedureId)) {
						mesIds.add(procedureId);
					}
					if("101".equals(ioType)) {
						log101List.add(log);
						continue;
					}
					if(StrUtil.isBlank(ioType)) {
						continue;
					}
					if(StrUtil.startWith(ioType, "2")) {
						StkIoMaterialLog inLog = log101List.get(0);
						String inEntryId = inLog.getEntityId();
						StkIoBillEntry inEntry = this.billEntryService.getById(inEntryId);
						String outEntryId = log.getEntityId();
						StkIoBillEntry outEntry = this.billEntryService.getById(outEntryId);
						if(null != inEntry && null != outEntry) {
							outEntry.setCost(inEntry.getCost());
							this.billEntryService.updateById(outEntry);
						}
					}
					if(StrUtil.startWith(ioType, "1")) {
						if(log101List.size() > 0) {
							StkIoMaterialLog stkIoMaterialLog = log101List.get(0);
							String returnEntryId = log.getEntityId();
							String preEntryId = stkIoMaterialLog.getEntityId();
							StkIoBillEntry preEntry = billEntryService.getById(preEntryId);
							StkIoBillEntry returnEntry = billEntryService.getById(returnEntryId);
							
							if(null != preEntry && null != returnEntry) {
								BigDecimal preQty = preEntry.getQty();
								BigDecimal preCost = preEntry.getCost();
								BigDecimal returnQty = returnEntry.getQty();
								BigDecimal returnCost = returnEntry.getCost();
								
								returnCost = preCost.multiply(returnQty).divide(preQty, 2, BigDecimal.ROUND_DOWN);
								returnCost = returnCost.setScale(2, BigDecimal.ROUND_DOWN);
								this.billEntryService.updateById(returnEntry);
							}
						}
						
						log101List.clear();
						log101List.add(log);
					}
				}
				
				if(CollUtil.isNotEmpty(log101List)) {
					StkIoMaterialLog ioMaterialLog = log101List.get(0);
					String inventoryEntryId = ioMaterialLog.getEntityId();
					StkIoBillEntry inventoryEntry = this.billEntryService.getById(inventoryEntryId);
					String batchNo = inventoryEntry.getBatchNo();
					String warehouseId = inventoryEntry.getWarehouseId();
					String mateId = inventoryEntry.getMaterialId();
					List<StkInventory> list = this.inventoryService.lambdaQuery().eq(StkInventory::getBatchNo, batchNo)
							.eq(StkInventory::getWarehouseId, warehouseId).eq(StkInventory::getMaterialId, mateId).list();
					if(CollUtil.isNotEmpty(list)) {
						StkInventory stkInventory = list.get(0);
						if(BigDecimal.ZERO.compareTo(stkInventory.getQty()) < 0) {
							stkInventory.setCost(inventoryEntry.getCost());
							this.inventoryService.updateById(stkInventory);
						}
					}
				}

				dealwithMesCost(mesIds);
			}
		}
	}

	private void dealwithMesCost(List<String> mesIds) {
		if(CollUtil.isEmpty(mesIds)) {
			return;
		}
		List<ZrkQzOrder> mesList = qzOrderService.lambdaQuery().in(ZrkQzOrder::getId, mesIds).list();
		if(CollUtil.isEmpty(mesList)) {
			return;
		}
		List<String> mesCodes = Lists.newArrayList();
		for(Iterator<ZrkQzOrder> it = mesList.iterator(); it.hasNext();) {
			ZrkQzOrder mes = it.next();
			String code = mes.getCode();
			mesCodes.add(code);
		}
		List<StkIoBill> billList = this.billService.getBillByMes(mesIds, mesCodes);
		if(CollUtil.isEmpty(billList)) {
			return;
		}
		Map<String, List<String>> materialMesMap = Maps.newHashMap();
		Map<String, List<String>> materialNotOneMesMap = Maps.newHashMap();
		Map<String, List<String>> mesMaterialIdMap = Maps.newHashMap();
		Map<String, List<StkIoBill>> mesBillMap = Maps.newHashMap();
		for(Iterator<StkIoBill> it = billList.iterator(); it.hasNext();) {
			StkIoBill bill = it.next();
			String ioType = bill.getStockIoType();
			String billId = bill.getId();
			Date createDate = bill.getCreateTime();
			String sourceId = bill.getSourceId();
			if(StrUtil.isBlank(sourceId)) {
				String sourceNo = bill.getSourceNo();
				if(StrUtil.isNotBlank(sourceNo)) {
					ZrkQzOrder byCode = this.qzOrderService.getByCode(sourceNo);
					sourceId = byCode.getId();
				}
			}
			if(StrUtil.isNotBlank(sourceId)) {
				List<StkIoBill> list = mesBillMap.get(sourceId);
				if(CollUtil.isEmpty(list)) {
					list = Lists.newArrayList();
					list.add(bill);
					mesBillMap.put(sourceId, list);
				} else {
					list.add(bill);
				}
			}
			
			List<StkIoBillEntry> entryList = billEntryService.selectByMainId(billId);
			for (StkIoBillEntry entry : entryList) {
				String materialId = entry.getMaterialId();
				List<StkIoMaterialLog> logList = logService.getByMaterial(materialId);
				List<String> materialMesIds = Lists.newLinkedList();
				if(CollUtil.isNotEmpty(logList)) {
					for (StkIoMaterialLog ioLog : logList) {
						String mesId = ioLog.getProcedureId();
						if(StrUtil.isBlank(mesId)) {
							continue;
						}
						if(materialMesIds.contains(mesId)) {
							continue;
						}
						materialMesIds.add(mesId);
						List<String> materialIds = mesMaterialIdMap.get(mesId);
						if(null == materialIds) {
							materialIds = Lists.newArrayList();
							materialIds.add(materialId);
							mesMaterialIdMap.put(mesId, materialIds);
						} else {
							if(!materialIds.contains(materialId)) {
								materialIds.add(materialId);
							}
						}
					}
				}
				materialMesMap.put(materialId, materialMesIds);
				if(materialMesIds.size() > 1) {
					materialNotOneMesMap.put(materialId, materialMesIds);
				}
			}
		}
		//核对出库成本
		List<String> dealOutMesIds = Lists.newLinkedList();
		if(materialNotOneMesMap.keySet().size() > 0) {
			for(String materialId : materialNotOneMesMap.keySet()) {
				List<String> list = materialNotOneMesMap.get(materialId);
			}
		}
		if(CollUtil.isNotEmpty(dealOutMesIds)) {
			
		}
		
		//最后核算入库成本
		for(String mesId : mesBillMap.keySet()) {
			List<StkIoBill> mesBills = mesBillMap.get(mesId);
			//第一遍计算所有的成本
			BigDecimal totalOutCost = BigDecimal.ZERO;
			BigDecimal totalInQty = BigDecimal.ZERO;
			List<StkIoBillEntry> inBillEntrys = Lists.newArrayList();
			for (StkIoBill bill : mesBills) {
				List<StkIoBillEntry> entrys = this.billEntryService.selectByMainId(bill.getId());
				String ioType = bill.getStockIoType();
				if(StrUtil.startWith(ioType, "2")) {
					if(null != entrys && 0 != entrys.size()) {
						for (StkIoBillEntry entry : entrys) {
							BigDecimal cost = entry.getCost();
							if(null != cost) {
								totalOutCost = totalOutCost.add(cost);
							}
						}
					}
					continue;
				}
				if(StrUtil.equals(ioType, "1994")) {
					if(null != entrys && 0 != entrys.size()) {
						for (StkIoBillEntry entry : entrys) {
							BigDecimal cost = entry.getCost();
							if(null != cost) {
								totalOutCost = totalOutCost.subtract(cost);
							}
						}
					}
					continue;
				}
				if(StrUtil.startWith(ioType, "1")) {
					if(null != entrys && 0 != entrys.size()) {
						for (StkIoBillEntry entry : entrys) {
							inBillEntrys.add(entry);
							BigDecimal qty = entry.getQty();
							if(null != qty) {
								totalInQty = totalInQty.add(qty);
							}
						}
					}
				}
			}
			if(CollUtil.isNotEmpty(inBillEntrys)) {
				for (StkIoBillEntry entry : inBillEntrys) {
					BigDecimal qty = entry.getQty();
					BigDecimal cost = totalOutCost.multiply(qty).divide(totalInQty, 2, BigDecimal.ROUND_DOWN);
					entry.setCost(cost);
					this.billEntryService.updateById(entry);
					String materialId = entry.getMaterialId();
					List<StkIoBillEntry> list = this.billEntryService.lambdaQuery().eq(StkIoBillEntry::getMaterialId, materialId).orderByDesc(StkIoBillEntry::getId).list();
					for (StkIoBillEntry theEntry : list) {
						String mid = theEntry.getMid();
						StkIoBill bill = this.billService.getById(mid);
						String ioType = bill.getStockIoType();
						if(StrUtil.startWith(ioType, "2")) {
							break;
						}
						if(StrUtil.equals(ioType, "1994")) {
							break;
						}
						if(StrUtil.equals(ioType, "301")) {
							continue;
						}
						if(StrUtil.startWith(ioType, "1")) {
							if(theEntry.getId().equals(entry.getId())) {
								String batchNo = entry.getBatchNo();
								List<StkInventory> list2 = inventoryService.lambdaQuery().eq(StkInventory::getBatchNo, batchNo).gt(StkInventory::getQty, BigDecimal.ZERO).list();
								if(CollUtil.isNotEmpty(list2)) {
									StkInventory inventory = list2.get(0);
									inventory.setCost(entry.getCost());
									this.inventoryService.updateById(inventory);
								}
							}
						}
					}
				}
			}
		}
	}
}
